if(typeof(QSAPI)=="undefined") var QSAPI={};
if(typeof(QSAPI.DataManager)=="undefined") QSAPI.DataManager = {};
if(!Util.isValid(GB_CURRENCY,true,true,true)){
	var GB_CURRENCY={
		USD:"USD",
		CAN:"CAN"
	};
}
if(!Util.isValid(GB_CURRENCY_DETAIL,true,true,true)){
	var GB_CURRENCY_DETAIL={
		USD: {
			"shortName":"US",
			"symbol":"$"
		},
		CAN: {
			"shortName":"CA",
			"symbol":"$"
		}
	};
}
if(!Util.isValid(GB_WL_KEY,true,true,true)){
	var GB_WL_KEY={
		//watchlistData item property
		RANK:"rank",
		ID:"rowID",
		ITEM:"item",
		DB:"inDB",
		//watchlist item property
		TICKER:"tickerObject",
		//tickerObject related property
		FIELD:"getField",
		GKEY:"gkey",
		VALID:"valid",
		PID:"performanceId",
		UTICKER:"clientTicker",//user ticker
		TTICKER:"ticker",//tenfore ticker
		SYMBOL:"symbol"
	};
}
if(!Util.isValid(GB_WL_TYPE,true,true,true)){
	var GB_WL_TYPE={
		WATCHLIST: "wl",
		PORTOFOLIO: "pf"
	};
}
if(!Util.isValid(GB_WL_GROUP,true,true,true)){
	var GB_WL_GROUP={
		SECTOR: "la03z",
		INDUSTRY: "st153",
		EXCHANGE: "exch",
		CURRENCY: "of028"
	};
}
if(!Util.isValid(GB_WL_MS,true,true,true)){
	var GB_WL_MS={
		DOW: "-1",
		SP50: "-2",
		US_INDEX:"-3",
		GLOBAL_INDEX:"-4",
		FOREX: "-7"
	};
}
if(!Util.isValid(GB_WL_LAYOUT,true,true,true)){
	var GB_WL_LAYOUT={
		LIST:"list",
		LIST_GROUP:"listgroup",
		LIST_WRAP:"listwrap",
		LITS_GROUP_WRAP:"listgroupwrap",
		HEATMAP:"heatmap"
	};
}
QSAPI.DataManager.WatchlistWidgetManager = function(){
	var _widgetMap =  {};
	var _activeMap =  {};//watchlist ID -> widgetIDs
	var _widgetActiveMap = {};//widgetID -> watchlist ID

	var _throwError=function(str){
		throw new Error(str);
	};
	return {
		register:function(widgetObject){
			if(!Util.isValid(widgetObject.subscribeID,true,true)){
				_throwError("WatchlistWidget need subscribeID.")
			}
			_widgetMap[widgetObject.subscribeID] = widgetObject;
		},
		activeWatchlist:function(widgetID,activeID){
			var oldActiveID = _widgetActiveMap[widgetID];
			_widgetActiveMap[widgetID]=activeID;
			if(Util.isValid(oldActiveID,true,true,true)){
				for(var i = 0,l= _activeMap[oldActiveID].length;i<l;i++){
					if(_activeMap[oldActiveID][i] == widgetID){
						_activeMap[oldActiveID].splice(i,1);
						break;
					}
				}
			}
			for(var widgetID in _widgetActiveMap){
				if(!_activeMap[activeID]){
					_activeMap[activeID]=[widgetID];
				}else{
					_activeMap[activeID].push(widgetID);
				}
			}
		},
		unregister:function(widgetObject){
			if(Util.isValid(widgetObject.subscribeID,true,true)){
				var activeID = _widgetActiveMap[widgetObject.subscribeID],widgetID = widgetObject.subscribeID;
				delete _widgetMap[widgetID];
				if(Util.isValid(activeID,true,true,true)){
					for(var i = 0,l= _activeMap[activeID].length;i<l;i++){
						if(_activeMap[activeID][i] == widgetID){
							_activeMap[activeID].splice(i,1);
							break;
						}
					}
				}
				delete _widgetActiveMap[widgetID];
			}
		},
		sync:function(watchlistId,functionName){
			var widget=null,widgetIDs= _activeMap[watchlistId];
			if(!Util.isValid(widgetIDs,true,true)) return;
			var widgetObject=null;
			for(var i=0,l=widgetIDs.length;i<l;i++){
				widgetObject = _widgetMap[widgetIDs[i]];
				if(Util.isFunction(widgetObject[functionName])){
					widgetObject[functionName]();
				}
			}
		}
	}
};
QSAPI.DataManager.WatchlistDataManager=function(){
	var _seedID=-10000;
	var _genItemID=function(){
		return --_seedID;
	};
	var _f=function(){};
	//
	var _config = {
		watchlist:{
			currency:GB_CURRENCY.USD
		},
		getWLtickers:"getdefaultwatchlist.jsp",//default url to get watchlist tickers
		getWLlist:"getdefaultwatchlist.jsp",//default url to get wl list
		maxTicker:{},
		includeMSWL:true
	};
	//default maxTicker
	_config["maxTicker"][GB_WL_TYPE.WATCHLIST]=200;
	_config["maxTicker"][GB_WL_TYPE.PORTOFOLIO]=200;
	//some key
	var _WL={
		ID:"id",
		NAME:"name",
		TYPE:"type",
		DATA:"data",
		CONFIG:"config",

		UTAG:"utag"
	};
	var _S={
		READY:"1",
		LOAD:"2",
		STOP:"3"
	};
	var _watchlistData_cache={};
	var _watchlistInfo_cache={};
	var _callback_cache={
		"getList":{
			MS:_S.STOP,
			CLIENTDB:_S.READY,
			CLIENT:_S.READY,
			CB:[]
		},
		"getData":{}
	};

	var _throwError=function(str){
		throw new Error(str);
	};
	var _extendProperty=function(watchlistItems,comeFromDB){
		var items=[],tempItem={};
		for(var i=0,l=watchlistItems.length;i<l;i++){
			if(!Util.isValid(watchlistItems[i][GB_WL_KEY.ID],true,true)){
				tempItem={};
				tempItem[GB_WL_KEY.ID]=_genItemID();
				tempItem[GB_WL_KEY.ITEM]=watchlistItems[i];
				tempItem[GB_WL_KEY.DB]=comeFromDB||false;//only for delete
				items.push(tempItem);
			}else{
				items.push(watchlistItems);
			}
		}
		return items;
	};
	var _catchWatchlist=function(wlJSON){//{id:"",name:"",type:"",data:"",config:{}}
		var id = wlJSON[_WL.ID];
		if(!_watchlistInfo_cache[id]){
			_watchlistInfo_cache[id] = {};
		}else{
			_watchlistInfo_cache[id][_WL.UTAG]=true;
		}
		_callback_cache["getData"][id] ={S:_S.STOP,CB:[]};
		delete wlJSON[_WL.ID];
		$.extend(true,_watchlistInfo_cache[id],{config:_config.watchlist},wlJSON);
		if(wlJSON[_WL.DATA]){
			_watchlistInfo_cache[id][_WL.DATA]=wlJSON[_WL.DATA].slice();
		}
	};
	var _execArray=function(array,arguments){
		if(array.length>0){
			var cb = array.shift();
			if(typeof cb == 'function'){
				cb(arguments);
			}
			_execArray(array,arguments);
		}
	};
	var _convertWLlistToArray=function(){//map to array only party of property
		var list=[],tempWatchlist={},needTolist=[_WL.NAME,_WL.TYPE,_WL.CONFIG];
		for(var wl in _watchlistInfo_cache){
			tempWatchlist={};
			tempWatchlist[_WL.ID]=wl;
			for(var i=needTolist.length-1;i>=0;i--){
				tempWatchlist[needTolist[i]]=_watchlistInfo_cache[wl][needTolist[i]];
			}
			list.push(tempWatchlist);
		}
		return list;
	};

	var _getWatchlistDataItems=function(sourceData,fromDB,callback){
		var callback=callback||_f;
		GB_WLC.genItems(sourceData,function(items){
			callback(_extendProperty(items,fromDB));
		});
	};

	var _genWatchlistDataFromSourceData=function(id,sourceData){
		var wlinfo=_watchlistInfo_cache[id];
		_getWatchlistDataItems(sourceData,(wlinfo[_WL.UTAG]?false:true),function(watchlistDataItems){
			if(wlinfo[_WL.UTAG]){//update
				GB_WLDM.deleteItems(id);
				GB_WLDM.insertItems(id,watchlistDataItems,false);//keep the sort
				delete wlinfo[_WL.UTAG];
			}else{
				_watchlistData_cache[id] = new QSAPI.DataManager.WatchlistData(id,wlinfo[_WL.NAME],wlinfo[_WL.TYPE],watchlistDataItems,$.extend(true,{},_config.watchlist,wlinfo[_WL.CONFIG]));
			}
			_callback_cache["getData"][id].S=_S.READY;
			_execArray(_callback_cache["getData"][id]["CB"],_watchlistData_cache[id]);
		});
	};
	var _listIsReady = function(){
		return _callback_cache["getList"]["MS"]==_S.READY&&
			   _callback_cache["getList"]["CLIENTDB"]==_S.READY&&
			   _callback_cache["getList"]["CLIENT"]==_S.READY
	};
	var _getWatchlistFromMS = function(callback){
		var data = {action:"getwatchlist"};
		$.getJSON(QSAPI.getQSPath() + _config.getWLlist +"?callback=?",
				data, function(watchlist){
					//convert to normal watchlist format
					var list=[],temp={},wl={};
					for(var i=0,l=watchlist.list.length;i<l;i++){
						wl = watchlist.list[i];
						temp={};
						temp[_WL.ID]=wl["watchId"];
						temp[_WL.NAME]=wl["watchName"];
						temp[_WL.TYPE]=GB_WL_TYPE.WATCHLIST;
						list.push(temp);
					}
					callback(list);
				});
	};
	var _getWatchDataFromMS = function(wlid,callback){
		var data = {
			action : "gettickers",
			watchId : wlid
		};
		$.getJSON(QSAPI.getQSPath() + _config.getWLtickers +"?callback=?",
				data, function(watchlistData){
					callback(watchlistData.tickers);
				});
	};
	var _dataSource={
		getList:function(para,callback){
			_getWatchlistFromMS(callback);
		},
		getWatchlistData:function(id,callback){
			_getWatchDataFromMS(id,callback);
		},
		saveWatchlist:function(array){

		}
	};
	return  {
		/*********set methed must invoke before GB_WLDM used***********/
		getMorningstarWLlist:function(callback){
			_getWatchlistFromMS(callback);
		},
		getMorningstarWLData:function(id,callback){
			_getWatchDataFromMS(id,callback);
		},
		setDataInterface:function(dataInterface){
			$.extend(true,_dataSource,dataInterface);
			if(Util.isFunction(_dataSource.getList)){
				_callback_cache["getList"]["CLIENTDB"] = _S.STOP;
			}
		},
		setConfig:function(config){//maxTicker,includeMSWL,watchlist
			$.extend(true,_config,config);
			if(!_config.includeMSWL){
				_callback_cache["getList"]["MS"]=_S.READY;
			}
		},
		setCustomColumns:function(data){
			if(Util.isValid(QSAPI.Widget.Columns,true,true)){
            	$.extend(true,QSAPI.Widget.Columns,data);
        	}
		},
		//from DB
		setWatchlistData:function(wlinfo){//[{id:"",name:"",type:""}]
			_callback_cache["getList"]["CLIENT"]=_S.LOAD;//gen list
			if($.isArray(wlinfo)){
				for(var i=0,l=wlinfo.length;i<l;i++){
					_catchWatchlist(wlinfo[i]);
				}
			}else{
				var tempwl=null;
				for(var wl in wlinfo){
					tempwl=wlinfo[wl];
					if(!_watchlistInfo_cache[wl]){
						_watchlistInfo_cache[wl] = {};
					}
					if(!_callback_cache[wl]){
						_callback_cache["getData"][wl] ={S:_S.STOP,CB:[]};
					}
					$.extend(true,_watchlistInfo_cache[wl],tempwl);
				}
			}
			var info=null;
			for(var id in _watchlistInfo_cache){
				_callback_cache["getData"][id].S=_S.LOAD;
				info = _watchlistInfo_cache[id];
				_genWatchlistDataFromSourceData(id,info[_WL.DATA]);
			}
			_callback_cache["getList"]["CLIENT"]=_S.READY;
			if(_listIsReady()){
				_execArray(_callback_cache["getList"]["CB"],_convertWLlistToArray());
			}
		},
		getMaxTickers:function(watchlistId){
			return _config.maxTicker[_watchlistInfo_cache[watchlistId][_WL.TYPE]];
		},
		getWatchlistDataItem:function(watchlistId,rowID){
			return _watchlistData_cache[watchlistId].getItem(rowID);
		},
		getWatchlistDataItems:function(sourceData,callback){
			_getWatchlistDataItems(sourceData,false,callback);
		},
		getRowIdsBySymbol:function(watchlistId,symbol){
			return _watchlistData_cache[watchlistId].getRowIDs(symbol);
		},
		getRowIdsByMSGK:function(watchlistId,gkey){
			return _watchlistData_cache[watchlistId].getRowIDsByMSGK(gkey);
		},
		insertItems:function(id,sourceData,isTop){//tickerObjects should be tickerObjects array
			if(!_watchlistData_cache[id]) return;
			if(sourceData[0][GB_WL_KEY.ITEM]){//watchlistDataItems
				_watchlistData_cache[id].insertItems(sourceData,isTop);
			}else{
				_getWatchlistDataItems(sourceData,false,function(watchlistDataItems){
					_watchlistData_cache[id].insertItems(watchlistDataItems,isTop);
				});
			}
		},
		updateItems:function(id,items){//items should be watchlistData item array
			if(!_watchlistData_cache[id]) return;
			_watchlistData_cache[id].updateItems(items);
		},
		deleteItems:function(id,rowIDs){
			if(!_watchlistData_cache[id]) return;
			_watchlistData_cache[id].deleteItems(rowIDs);
		},
		moveItem:function(id,rowID,targetRowID,targetIsBefore){
			if(!_watchlistData_cache[id]) return;
			_watchlistData_cache[id].moveItem(rowID,targetRowID,targetIsBefore);
		},
		sortItems:function(id,rowIDs){
			if(!_watchlistData_cache[id]) return;
			_watchlistData_cache[id].sortItems(rowIDs);
		},
		deleteWatchlist:function(id){

		},
		addWatchlist:function(){

		},
		updateWatchlistInfo:function(){

		},
		saveWatchlist:function(id){
			var getSaveData=function(id){
				var temp={},data=null;
				temp[_WL.ID]=id;
				temp[_WL.DATA] = _watchlistData_cache[id].saveItems();
				return temp;
			}
			var saveConfig=[],saveItem=null;
			if(Util.isValid(id,true,true,true)){
				saveItem = getSaveData(id);
				if(Util.isValid(saveItem,true,true)){
					saveConfig.push(saveItem);
				}
			}else{
				for(var id in _watchlistData_cache){
					saveItem = getSaveData(id);
					if(Util.isValid(saveItem[_WL.DATA],true,true)){
						saveConfig.push(saveItem);
					}
				}
			}
			_dataSource.saveWatchlist(saveConfig);
			return saveConfig;
		},
		getWatchlistList:function(para,callback){//get list by data interface
			var callback=callback||_f;
			if(_listIsReady()){
				callback(_convertWLlistToArray());
			}else{
				_callback_cache["getList"]["CB"].push(callback);
				if(_callback_cache["getList"]["MS"]==_S.STOP||_callback_cache["getList"]["CLIENTDB"]==_S.STOP){
					if(_callback_cache["getList"]["MS"]==_S.STOP){
						_callback_cache["getList"]["MS"]=_S.LOAD;
					}
					if(_callback_cache["getList"]["CLIENTDB"]==_S.STOP){
						_callback_cache["getList"]["CLIENTDB"]==_S.LOAD;
					}
					_dataSource.getList(para,function(watchlistlist){//[{id:"",name:"",type:""}]
						//it is necessary that all watchlist list are ready, it can execute the callback
						for(var i=0,l=watchlistlist.length;i<l;i++){
							_catchWatchlist(watchlistlist[i]);
						}
						_callback_cache["getList"]["MS"]=_S.READY;
						_callback_cache["getList"]["CLIENTDB"]=_S.READY;
						if(_listIsReady()){
							_execArray(_callback_cache["getList"]["CB"],_convertWLlistToArray());
						}
					});
				}
			}
		},
		getWatchlistData:function(id,callback){
			if(!_watchlistInfo_cache[id]){
				_throwError("Don't watchlist information["+id+"]");
			}
			var callback=callback||_f;
			var watchlistInfo = _watchlistInfo_cache[id];
			if(_callback_cache["getData"][id].S==_S.READY){
				callback(_watchlistData_cache[id]);
			}else{
				_callback_cache["getData"][id]["CB"].push(callback);
				if(_callback_cache["getData"][id].S==_S.STOP){
					with({ID:id}){
						_callback_cache["getData"][id].S=_S.LOAD;
						_dataSource.getWatchlistData(ID,function(sourceData){//tickerObject
							_watchlistInfo_cache[ID][_WL.DATA]=sourceData;//source data
							_genWatchlistDataFromSourceData(ID,_watchlistInfo_cache[ID][_WL.DATA]);
						});
					}
				}
			}
		}
	}
};

QSAPI.DataManager.WatchlistItemCreator=function(){
	var _throwError=function(str){
		throw new Error(str);
	};
	var _config ={TICKER:"symbol"};
	var _f=function(){};
	var _genItem = function(item){
		if(!Util.isValid(item[GB_WL_KEY.TICKER],true,true,true)){
			_throwError("WatchlistItem need "+GB_WL_KEY.TICKER+" attribute");
		}
		return QSAPI.DataManager.WatchlistItem(item);
	};

	var _convertTickerObject = function(tickers,callback){
		QSAPI.TickerFactory.create(tickers,{
			onSuccess:function(tickerObjects){
				callback(tickerObjects);
			}
		});
	};

	var _isComplexJson=function(obj){
		if(typeof obj!='object'){
			return false;
		}
		if(Util.isFunction(obj[GB_WL_KEY.FIELD])){//is tickerObject
			return false;
		}
		var count=0;
		for(var i in obj){
			count++;
		}
		if(count>1){
			return true;
		}
		return false;
	};
	var _genWatchlistItems=function(tickerObjects,callback){
		var callback=callback||_f;
		if(typeof tickerObjects[0]!='object'){//if tickerObject is string array
			_convertTickerObject(tickerObjects,function(tickers){
				var items=[],item={};
				for(var i=0,l=tickers.length;i<l;i++){
					item[GB_WL_KEY.TICKER] = tickers[i];
					items.push(_genItem(item));
				}
				callback(items);
			});
		}else{//if tickerObject is tickerObject array
			var items=[],item={};
			for(var i=0,l=tickerObjects.length;i<l;i++){
				item[GB_WL_KEY.TICKER] = tickerObjects[i];
				items.push(_genItem(item));
			}
			callback(items);
		}
	};
	var _genCustomWatchlistItems=function(itemArray,callback,config){//if itemArray is a json including GB_WL_KEY.TICKER property
		var callback=callback||_f;
		var tickers=[];
		if(itemArray[0].hasOwnProperty(_config.TICKER)){//all have the symbol property
			for(var j=0,l=itemArray.length;j<l;j++){
				tickers.push(itemArray[j][_config.TICKER]);
			}
			_convertTickerObject(tickers,function(tickerObjects){
				var items=[];
				for(var i=0,l=itemArray.length;i<l;i++){
					itemArray[i][GB_WL_KEY.TICKER]=tickerObjects[i];
					items.push(_genItem(itemArray[i]));
				}
				callback(items);
			});
		}else{
			for(var i=0,l=itemArray.length;i<l;i++){//include tickerObject attribute
				tickers.push(_genItem(itemArray[i]));
			}
			callback(tickers);
		}
	};
	return {
		genItems:function(itemArray,callback,config){
			if((!Util.isValid(itemArray,true,true))||itemArray.length==0) {callback([]); return;}
			if(!_isComplexJson(itemArray[0])){//include muti-property json in array item
				_genWatchlistItems(itemArray,callback);
			}else{//array item is  ticker string or tickerObject
				_genCustomWatchlistItems(itemArray,callback,$(true,{},_config,config));
			}
		}
	}
};

QSAPI.DataManager.WatchlistItem=function(itemJson){
	var _tickerObject=itemJson[GB_WL_KEY.TICKER];//only
	var _referID = [GB_WL_KEY.GKEY,GB_WL_KEY.VALID];//only check this two
	var _isValid=true;
	var _checkValid = function(){
		for(var j=0,m=_referID.length;j<m;j++){
			tempID = _tickerObject[GB_WL_KEY.FIELD](_referID[j]);
			if(!Util.isValid(tempID,true,true,true)||!tempID){//false
				_isValid=false;
				break;
			}
		}
	}
	var _customProperty={};
	delete itemJson[GB_WL_KEY.TICKER];
	(function(itemJson){
		$.extend(true,_customProperty,itemJson);
	})(itemJson);
	_checkValid();
	return {
		getSymbol:function(){
			return _tickerObject[GB_WL_KEY.FIELD](GB_WL_KEY.SYMBOL);
		},
		getTickerObject:function(){
			return _tickerObject;
		},
		getMSGK:function(){
			return _tickerObject[GB_WL_KEY.FIELD](GB_WL_KEY.GKEY);
		},
		isValid:function(){
			return _isValid;
		},
		updateCustomColumn:function(columnName,columnValue){
			if(_customProperty.hasOwnProperty(columnName)){
				_customProperty[columnName]=columnValue;
			}
		},
		getCustomColumns:function(field){
			if(Util.isValid(field,true,true,true)){
				return _customProperty[field];
			}else{
				return $.extend(true,{},_customProperty);
			}
		},
		getItemData:function(){
			var item={};
			item[GB_WL_KEY.TICKER]=_tickerObject;
			return $.extend(true,item,_customProperty);
		}
	}
};

QSAPI.DataManager.WatchlistData=function(id,name,type,items,userConfig){
	//user data config
	var _config={rankID:"rank"};
	$.extend(true,_config,userConfig);
	var _id  = id;
	var _name = name;
	var _type = type;
	var _symbolMap = {};//symbol -> rowID array
	var _gkeyMap = {};//gkey -> rowID array
	var _itemMap={};//rowID -> wlData item
	var _wlItemMap={};//rowID -> wlitem
	var _invalid = [];//symbols
	var _items = [];//sortable
	var _deletedItems={};
	var _cmd ={
		ADD:"add",//row id
		DEL:"delete",//row id
		MOV:"move",
		UPT:"update" //row id
	};
	var _actionStack={};

	var _sortByRank = function(){
		if(_items.length>0){
			_items.sort(function(itemA,itemB){
				var rankA = itemA[GB_WL_KEY.RANK];
				var rankB = itemB[GB_WL_KEY.RANK];
				return rankA>rankB?1:-1;
			});
		}
	};
	var _deleteItem = function(rowID){
		delete _wlItemMap[rowID];//delete watchlist Item map
		delete _itemMap[rowID];//delete watchlistData Item map
		var item = null, wlitem=null,symbol=null,gkey=null;
		for(var j=_items.length-1;j>=0;j--){
			item=_items[j];
			if(item[GB_WL_KEY.ID] == rowID){
				_items.splice(j,1);//delete watchlistData Item
				wlitem = item[GB_WL_KEY.ITEM];
				symbol = wlitem.getSymbol();
				if(wlitem.isValid()){//delete gkey cache
					gkey = wlitem.getMSGK();
					_deleteArray(_gkeyMap[gkey],rowID);
					if(_gkeyMap[gkey].length==0){
						delete _gkeyMap[gkey];
					}
				}			
				_deleteArray(_symbolMap[symbol],rowID);//delete symbol cache
				if(_symbolMap[symbol].length==0){
					delete _symbolMap[symbol];
					_deleteArray(_invalid,symbol);
				}
				_actionStack[rowID]={};//record the delete operator
				if(item[GB_WL_KEY.DB]){
					_deletedItems[rowID] = wlitem;
					_actionStack[rowID][_cmd.DEL]=true;
				}
				break;
			}
		}
	};
	var _insertItem = function(item,isTop){
		var rank = item[GB_WL_KEY.ITEM].getCustomColumns(_config.rankID);
		var includeRank = Util.isValid(rank,true,true,true);//only will be used in load at first
		if(!includeRank){
			if(isTop){
				if(_items.length>0){
					rank = _genRank(null,_items[0][GB_WL_KEY.RANK]);
				}else{
					rank = 0;
				}
			}else{
				if(_items.length>0){
					rank = _genRank(_items[_items.length-1][GB_WL_KEY.RANK],null);
				}else{
					rank = 0;
				}
			}
		}
		item[GB_WL_KEY.RANK] = rank;
		isTop?_items.unshift(item):_items.push(item);
		var wlitem =  item[GB_WL_KEY.ITEM];
		var rowID = item[GB_WL_KEY.ID];
		_itemMap[rowID] = item;
		_wlItemMap[rowID] = wlitem;
		var symbol =  wlitem.getSymbol(),gkey = wlitem.getMSGK();
		if(!wlitem.isValid()&&$.inArray(symbol,_invalid)<0){
			_invalid.push(symbol);
		}else{
			if(!_gkeyMap[gkey]){
				_gkeyMap[gkey]=[rowID];
			}else{
				_gkeyMap[gkey].push(rowID);
			}
		}
		if(!_symbolMap[symbol]){
			_symbolMap[symbol]=[rowID];
		}else{
			_symbolMap[symbol].push(rowID);
		}
		if(!_actionStack[rowID]){
            _actionStack[rowID]={};
        }
        if(!item[GB_WL_KEY.DB]){
        	_actionStack[rowID][_cmd.ADD]=true;
        	// console.log(rowID);
        	// console.dir(_actionStack[rowID]);
        }
	};

	var _deleteArray=function(array,value){
		for(var h=array.length;h>=0;h--){
			if(array[h] == value){
				array.splice(h,1);
			}
		}
	};

	var _genRank=function(low,high){
		if(!Util.isValid(high,true,true)){
			return low+1;
		}
		if(!Util.isValid(low,true,true)){
			return high-1;
		}
		return (low+high)/2;
	};

	var _refreshRank = function(){
		var rank = 0,wlitem=null;
		for(var i=0,l=_items.length;i<l;i++){
			rank = i;
			_items[i][GB_WL_KEY.RANK]=i;
			_wlItemMap[_items[i][GB_WL_KEY.ID]].updateCustomColumn(_config.rankID,rank);
		}
	};

	var _insertItems = function(items,isTop){
		if(items.length==0) return;
		for(var i=0,l=items.length;i<l;i++){
			_insertItem(items[i],isTop);
		}
	};
	_insertItems(items,false);
	_sortByRank();
	return {
		getItems:function(){
			return _items;
		},
		getItem:function(rowID){
			return _itemMap[rowID];
		},
		getWLItem:function(rowID){
			return _wlItemMap[rowID];
		},
		getRowIDs:function(symbol){
			return _symbolMap[symbol]||[];
		},
		getRowIDsByMSGK:function(gkey){
			return _gkeyMap[gkey]||[];
		},
		saveItems:function(){
			//_actionStack
			// console.log("watchlist ID:"+_id);
			// console.dir($.extend(true,{},_actionStack));
			var action=null,delRows=[],item={},addRows=[],updateRows=[],moveRows=[],wlItem=null,returnValue=null,rank={};
			for(var rowID in _actionStack){
				action = _actionStack[rowID];
				wlItem =  _wlItemMap[rowID];
				if(action[_cmd.DEL]){//delete
					delRows.push(_deletedItems[rowID].getItemData());
				}else if(action[_cmd.ADD]){
					addRows.push(wlItem.getItemData());
				}else if(action[_cmd.UPT]){
					updateRows.push(wlItem.getItemData());
				}else if(action[_cmd.MOV]){
					item = wlItem.getItemData();
					if(!Util.isValid(wlItem.getCustomColumns(_config.rankID),true,true)){
						item[GB_WL_KEY.RANK] = _itemMap[rowID][GB_WL_KEY.RANK];
					}
					moveRows.push(item);
				}
				_actionStack[rowID]={};
			}
			if(delRows.length>0||addRows.length>0||updateRows.length>0||moveRows.length>0){
				returnValue={};
				if(delRows.length>0){
					returnValue[_cmd.DEL]=delRows;
				}
				if(addRows.length>0){
					returnValue[_cmd.ADD]=addRows;
				}
				if(updateRows.length>0){
					returnValue[_cmd.UPT]=updateRows;
				}
				if(moveRows.length>0){
					returnValue[_cmd.MOV]=moveRows;
				}
			}
			// console.dir(returnValue);
			return returnValue;
		},
		insertItems:function(items,isTop){
			_insertItems(items,isTop);
		},
		sortItems:function(rowIDs){//sort all rank
			_items=[];
			for(var i=0,l=rowIDs.length;i<l;i++){
				_items.push(_itemMap[rowIDs[i]]);
				_actionStack[rowIDs[i]][_cmd.UPT]=true;
			}
			_refreshRank();
		},
		moveItem:function(moveID,targetRowID,targetIsBefore){
			if(moveID==targetRowID) return;
			var rowID=null,item=null,updatedItem=null,sourceIndex=null,targetIndex=-1,count=0;
			var l=_items.length,low=null,high=null;
			var isBefore=Util.isValid(targetIsBefore,true,true,true)&&targetIsBefore;//move to target below
			if(!Util.isValid(targetRowID,true,true,true)){
			    if(isBefore){//first
			    	high = _items[0][GB_WL_KEY.RANK];
			    }else{//last
			    	low = _items[_items.length-1][GB_WL_KEY.RANK];
			    }
			    for(var i=0;i<l;i++){
					rowID = _items[i][GB_WL_KEY.ID];
					if(rowID == moveID){
						sourceIndex = i;
						break;
					}
				}
			}else{
				for(var i=0;i<l;i++){
					item = _items[i];
					rowID = item[GB_WL_KEY.ID];
					if(rowID == targetRowID){
						targetIndex=i;
						if(isBefore){
							low=_items[i][GB_WL_KEY.RANK];
							if(i+1<l){
								high=_items[i+1][GB_WL_KEY.RANK];
							}
						}else{
							high=_items[i][GB_WL_KEY.RANK];
							if(i-1>=0){
								low=_items[i-1][GB_WL_KEY.RANK];
							}
						}
						count++;
					}
					if(rowID == moveID){
						sourceIndex = i;
						count++
					}
					if(count==2){
						break;
					}
				}
			}
			if((isBefore&&targetIndex+1==sourceIndex)||(!isBefore&&targetIndex-1==sourceIndex)) return;
			if(Util.isValid(sourceIndex,true,true)){
				var moveItems=null;
				moveItems = _items.splice(sourceIndex,1);
				moveItems[0][GB_WL_KEY.RANK] = _genRank(low,high);
				moveItems[0][GB_WL_KEY.ITEM].updateCustomColumn(_config.rankID,moveItems[0][GB_WL_KEY.RANK]);
				if(isBefore){
					_items.splice(sourceIndex>targetIndex?targetIndex+1:targetIndex,0,moveItems[0]);
				}else{
					_items.splice(sourceIndex>targetIndex?targetIndex:targetIndex-1,0,moveItems[0]);
				}
				_actionStack[moveID][_cmd.MOV]=true;
				// console.log(moveID);
				// console.dir(_actionStack[moveID]);
			}
		},
		updateItems:function(items){
			var rowID=null,item=null,updatedItem=null;
			for(var i=0,l=_items.length;i<l;i++){
				rowID = _items[i][GB_WL_KEY.ID];
				for(var i=0,l=items.length;i<l;i++){
					item = items[i];
					if(rowID==item[GB_WL_KEY.ID]){
						_wlItemMap[rowID].setCustomColumns(item[GB_WL_KEY.ITEM].getCustomColumns());
						if(!_actionStack[rowID][_cmd.ADD]){
							_actionStack[rowID][_cmd.UPT]=true;
							// console.log(rowID);
							// console.dir(_actionStack[rowID]);
						}
					}
				}
			}
		},
		deleteItems:function(rowIDs){
			if(Util.isValid(rowIDs,true,true)){
				for(var i=0,l=rowIDs.length;i<l;i++){
					_deleteItem(rowIDs[i]);
				}
			}else{
				for(var j=_items.length-1;j>=0;j--){
					_deleteItem(_items[j][GB_WL_KEY.ID]);
				}
			}
		}
	}
};

/*init wl global object*/
if(!Util.isValid(GB_WLC,true,true,true)){
	var GB_WLC = new QSAPI.DataManager.WatchlistItemCreator();//for datamanager
}

if(!Util.isValid(GB_WLDM,true,true,true)){
	var GB_WLDM = new QSAPI.DataManager.WatchlistDataManager();
}

if(!Util.isValid(GB_WLWM,true,true,true)){
	var GB_WLWM = new QSAPI.DataManager.WatchlistWidgetManager();
}